#include "../coro_call.h"

namespace coroutine {

std::int32_t CoroProtocol::getError() {
    return errorID_;
}

bool CoroProtocol::send(const char* data, std::size_t size) {
    id_ = rc().getCurrentCallID();
    return send_(id_, data, size);
}

CallID CoroProtocol::getID() const {
    return id_;
}

CallResult CoroProtocol::call() {
    type_ = CallType::NO_ARG_RET;
    return rc().call(this, nullptr, 0);
}

CallResult CoroProtocol::call(std::time_t timeout) {
    type_ = CallType::NO_ARG_RET_TIMEOUT;
    return rc().call(this, nullptr, 0, timeout);
}

void CoroProtocol::error(std::int32_t error) {
    if (error) {
        errorID_ = error;
    }
    rc().call_return(id_);
}

void CoroProtocol::success() {
    errorID_ = ERROR_OK;
    rc().call_return(id_);
}

void Call::clear() {
    waitCalls_.clear();
}

CallResult Call::call(CoroProtocol* sender, const char* data, std::size_t size) {
    if (coro_is_main()) {
        return CallResult::FAIL;
    }
    if (!sender->send(data, size)) {
        return CallResult::FAIL;
    }
    waitCalls_.emplace(rcID_++, coro_id());
    coro_pop_wait();
    if (sender->getError() != ERROR_OK) {
        return CallResult::FAIL;
    }
    return CallResult::OK;
}

CallResult Call::call(CoroProtocol* sender, const char* data,
    std::size_t size, std::time_t timeout) {
    if (coro_is_main()) {
        return CallResult::FAIL;
    }
    if (!sender->send(data, size)) {
        return CallResult::FAIL;
    }
    waitCalls_.emplace(rcID_++, coro_id());
    coro_pop_wait(timeout);
    if (sender->getError() == ERROR_NOT_SET) {
        return CallResult::TIMEOUT;
    } else if (sender->getError() != ERROR_OK) {
        return CallResult::FAIL;
    }
    return CallResult::OK;
}

void Call::call_return(CallID rcID) {
    if (!coro_is_main()) {
        return;
    }
    auto it = waitCalls_.find(rcID);
    if (it == waitCalls_.end()) {
        return;
    }
    auto coroID = it->second;
    waitCalls_.erase(it);
    coro_resume(coroID);
}

}
